Дана
последовательность из n целых чисел a1, a2, ..., an.
Для каждого элемента ak (k = 1, 2, ..., n) мы находим первый элемент правее ak, больший его (если такой существует). Обозначим такой
элемент ak1.
Затем сделаем то же самое для элемента ak1
и обозначим найденный элемент ak2,
и так далее пока последовательность не закончится. Таким образом формируется
подпоследовательность ak1,
ak2, ...,
которую мы назовем цепью, начинающейся с индекса k.
Напишите
программу, которая выводит для каждого индекса k длину соответствующей цепи, начинающейся с индекса k.
Вход. В первой
строке записано натуральное число n
(0 < n ≤ 500000). Во второй
строке даны элементы последовательности a1,
a2, ..., an (0 < ai < 106).
Выход. В одной
строке выведите последовательность длин цепей, соответствующих элементам
входных данных.
Пример входа |
Пример выхода |
10 3 2 4 2 11 2 7 5 8 10 |
2 2 1 1 0 3 2 2 1 0 |
стек
Объявим
массив nex такой что nex[i] содержит
индекс ближайшего справа элемента, большего ai.
Например, nex[i] = 0 означает что
справа от ai нет
элементов, больших ai.
Объявим
стек s, который вместо чисел ai
будет хранить индексы i. Занесем в
стек первый элемент: s.push(1) – заносим индекс первого элемента.
Последовательно обрабатываем остальные числа последовательности. Пусть текущим
элементом является aj.
Тогда:
·
если a[s.top()]
≥ a[j], то индекс j кладем в
стек: s.push(j);
·
иначе пока стек не пустой и a[s.top()] < a[j], извлекаем элемент из стека i = s.top() и устанавливаем nex[i] = j.
По завершению цикла j заносим в стек:
s.push(j);
Теперь по
массиву nex следует восстановить результирующую последовательность dp, где dp[i] равно длине цепи, начинающейся с
индекса i. Двигаемся с конца массива
в начало (i = n, n – 1, …, 2,
1) и вычисляем значение dp[i]:
·
если nex[i] =
0, то dp[i] = 0;
·
иначе dp[i] =
dp[nex[i]] + 1;
Отметим,
что для вычисления dp[i] значение dp[nex[i]]
уже должно быть найдено, поэтому вычисление ячеек массива dp следует выполнять
справа налево.
Пример
Заполняем
массив dp справа налево.
Реализация алгоритма
Объявим
рабочие массивы.
#define MAX 500001
int a[MAX], nex[MAX], dp[MAX];
stack<int> s;
Основная часть программы. Читаем входные данные.
scanf("%d",&n);
for(i = 1; i <= n; i++)
scanf("%d",&a[i]);
Заносим в стек индекс первого элемента – единицу.
s.push(1);
Последовательно обрабатываем остальные элементы.
for(j = 2; j <= n; j++)
{
while(!s.empty())
{
i = s.top();
if(a[i] >= a[j]) break;
nex[i] = j;
s.pop();
}
s.push(j);
}
Пересчитываем результирующий массив.
dp[n] = 0;
for(i = n - 1; i >= 1; i--)
if (nex[i] == 0) dp[i] = 0;
else dp[i] = 1 + dp[nex[i]];
Выводим ответ – длины цепей, начинающихся с позиции i.
for(i = 1; i <= n; i++)
printf("%d
",dp[i]);
printf("\n");
Java реализация
import java.util.*;
public class Main
{
public static void main(String[] args)
{
Scanner con = new
Scanner(System.in);
int n = con.nextInt();
int a[] = new int[n+1];
int next[] = new int[n+1];
int dp[] = new int[n+1];
for(int i = 1; i <= n; i++)
a[i] = con.nextInt();
Stack<Integer> s = new
Stack<Integer>();
s.push(1);
for(int j = 2; j <= n; j++)
{
while(!s.empty())
{
int i = s.peek();
if(a[i] >= a[j]) break;
next[i] = j;
s.pop();
}
s.push(j);
}
dp[n] = 0;
for(int i = n - 1; i >= 1; i--)
if (next[i] == 0) dp[i] = 0;
else dp[i] = 1 + dp[next[i]];
for(int i = 1; i <= n; i++)
System.out.print(dp[i] + " ");
System.out.println();
con.close();
}
}